;*****************************************************************************************************
;+
; NAME:
;
;      eIMAGE (enhanced IMAGE)
;
; PURPOSE:
;
;      This is the enhanced version of the standard IDL TV
;      command. The main difference with TV is that it sets up a
;      coordinate system for the image. That means that you can use
;      the POSITION and XRANGE/YRANGE keywords like the standard PLOT
;      command. This is done to easily position the image in the
;      window and to be able to overplot other plotting commands
;      (typically contours and axis) on top of the image.
;
; CALLING SEQUENCE:
;
;      eIMAGE, img
;
; ARGUMENTS:
;
;     IMG:          A 2 or 3-dimensional array that contains the image
;                   pixel values. If the array has 3 dimensions, it is
;                   treated automatically as a True-Color image and
;                   there is no need to precise the interleaving state
;                   (unlike TV). 
;                 
; KEYWORD PARAMETERS:
;      
;
;     POSITION:     4-element vector specifying the position of the
;                   image in normalized coordinates. The default is
;                   !IGL_P.position
;
;     XRANGE:       2-element vector specifying the data X range to
;                   associate with the image. The default is to use
;                   [0,Xsize-1] where Xsize is the image size in the X
;                   direction. 
;
;     YRANGE:       2-element vector specifying the data Y range to
;                   associate with the image. The default is to use
;                   [0,Ysize] where Ysize is the image size in the Y
;                   direction. 
;
;     STATIC:       Set this keyword to indicate that
;                   the data coordinate system associated with the
;                   image is static. This prevents the data X and Y
;                   ranges to be changed.
;
;     SAVE:         Set this keyword to save the newly established
;                   coordinate system. Subsequent IGL overplotting
;                   commands will use the image coordinate system.
;
;     NODRAW:       Set this keyword to prevent the
;                   current window to be refreshed. The image
;                   is added to the window but will not be
;                   visible until the window is refreshed (by calling
;                   another IGL plotting command for example). This is
;                   useful is you wish to issue several plotting
;                   commands but want to display the results only at
;                   the end.
;
;    NOERASE:       If set, prevent the current window to be erased
;                   before drawing the image. The default is
;                   !IGL_P.noerase
;
;    NOINTERPOLATE:    If the image is resized to fit the window, this keyword, if set, will
;                      cause nearest neighbor interpolation of image values to be used. The
;                      default is to use  bilinear interpolation. 
;
;    KEEP_ASPECT:      If this keyword is set, the POSITION of the image in the window
;                      is always adjusted to preserve the image aspect ratio (ratio of height
;                      divided by width). Otherwise, the image is resized arbitrarily.
;
;    AXES:          Set this keyword to add axes to the image.
;
;    The following keywords apply only if the AXES keyword is set.
;
;    AXISCOLOR:    The name of the foreground color for the axis. The
;                  default is !IGL_P.axiscolor
;
;    CHARSIZE:     The character size for the annotations (float). The default
;                  is !IGL_P.charsize
;
;    CHARTHICK:    The character thickness for the annotations (integer). The
;                  default is !IG_P.charthick
;
;    FONT:         The character font for the annotations(integer).
;                  -1 : Herschey, 0 : Hardware, 1 : True Type. The
;                  default is !IGL_P.font
;
;    TICKLEN:      Specify the length of the axis tick marks, in
;                  units of the fraction of the plot window. The
;                  default is !IGL_P.ticklen.
;
;    [XY]CHARSIZE: The character size for the axis annotations. Be
;                  careful that this is like the regular IDL
;                  keyword meaning that it only indicates a multiplying
;                  factor to the global character size specified by
;                  the CHARSIZE keyword. The default is !IGL_[XY].charsize
;
;    [XY]GRIDSTYLE: Integer specifying the linestyle to use for the
;                   axis grid. The default is !IGL_[XY].gridstyle
;
;    [XY]LOG:      Set this keyword if you want logarithmic axis
;                  scaling.
;
;    [XY]MINOR:    The number of minor axis tick intervals. The default
;                  is !IGL_[XY].minor
;
;    [XY]THICK:    The thickness of the line forming the axis. The
;                  default is !IGL_[XY].thick
;
;    [XY]TICKFORMAT: String specifying the format used for the axis
;                    labels. The default is !IGL_[XY].tickformat
;
;    [XY]TICKINTERVAL: Scalar indicating the interval between major
;                      tick marks. The default is !IGL_[XY].tickinterval
;
;    [XY]TICKLAYOUT: Integer specifying the axis layout. Possible
;                    values are : 0 = Normal layout, 1 = Only labels,
;                    2 = Box layout. The default is !IGL_[XY].ticklayout
;
;    [XY]TICKLEN:  The length of the axis tick marks. This overrides
;                  the global setting specified by the TICKLEN
;                  keyword. The default is !IGL_[XY].ticklen
;
;    [XY]TICKNAME: String array specifying the annotation of each
;                  tick marks. The default is !IGL_[XY].tickname
;
;    [XY]TICKS:    Integer specifying the number of major tick
;                  intervals. The default is !IGL_[XY].ticks
;
;    [XY]TICKUNITS: Vector of strings indicating the units used for
;                   axis tick labeling. The default is !IGL_[XY].tickunits
;
;    [XY]TICKV:    Array of data values for each tick mark. The
;                  default is !IGL_[XY].tickv
;
;    [XY]TITLE:    The text string used as the axis title. The
;                  default is !IGL_[XY].title     
;
;    The following keywords apply only for indexed color images (2-D arrays)
;
;    BOTTOM:        The lowest value of the byte-scaled display image.
;
;    BETA:          The beta factor in a Hyperpolic Sine stretch. Default is 3.0.
;
;    EXPONENT:      The logarithm exponent in a logarithmic stretch. Default is 4.0.
;
;    GAMMA:         The gamma factor in a gamma stretch. Default is 1.5.
;
;    MEAN:          The mean factor in a logarithmic stretch. Default is 0.5.
;     
;    MISSING_COLOR: The color name of the missing value. By default, "black".
;
;    MISSING_INDEX: The index of the missing color in the final byte scaled image. 
;                   By default, 255.
;     
;    MISSING_VALUE: The number that represents the missing value in the image.
;
;    NCOLORS:       The number of colors to scale the data into. (Default: 256 unless a
;                    MISSING_VALUE is used, in which case 255.)
;
;    NEGATIVE:      Set this keyword if you want to display the image with a negative or reverse stretch.
;
;    SCALETYPE:     The type of scaling performed prior to display. Default is 0, linear scaling.
;                    May be specified as a number or as a string (e.g, 3 or "ASINH").
;
;           Number   Type of Stretch
;             0         Linear         scaled = BytScl(image, MIN=minThresh, MAX=maxThresh)
;             1         Gamma          scaled = GmaScl(image, MIN=minThresh, MAX=maxThresh, Gamma=gamma)
;             2         Log            scaled = LogScl(image, MIN=minThresh, MAX=maxThresh, Mean=mean, Exponent=exponent)
;             3         Asinh          scaled = AsinhScl(image, MIN=minThresh, MAX=maxThresh, Beta=beta)
;             4         Linear 2%      A linear stretch, with 2 percent of pixels clipped at both the top and bottom
;             5         Square Root    A linear stretch of the square root histogram of the image values.
;             6         Equalization   A linear stretch of the histogram equalized image histogram.
;             7         Gaussian       A Gaussian normal function is applied to the image histogram.
;             8         None           No scaling whatsoever is done.
;             9         MODIS          Scaling done in the differential manner of the MODIS Rapid Response Team
;                                      and implemented in the Coyote Library routine ScaleModis.
;
;    SCLMIN:        The image data is scaled between SCLMIN and SCLMAX before display. Default is MIN(image).
;
;    SCLMAX:        The image data is scaled between SCLMIN and SCLMAX before display. Default is MAX(image).
;
;    SIGMA:         The sigma scale factor in a Gaussian
;                   stretch. Default is 1.0.
;
;    
; PACKAGE:
;
;    Interactive Graphics Library (IGL)
;
; AUTHORS:
;
;        RAPHAEL SADOUN
;        Graduate Student
;        Institut d'Astrophysique de Paris
;        98 bis boulevard Arago
;        75014 PARIS FRANCE
;        E-mail: raphael.sadoun@gmail.com
;
;
; MODIFICATION HISTORY:
;
;     Written by: Raphael Sadoun, 24 April 2012
;-
;*****************************************************************************************************
PRO eIMAGE, img, $
            POSITION=position, $
            XRANGE=xrange, $
            YRANGE=yrange, $
            SAVE=save, $
            STATIC=static, $
            NODRAW=nodraw, $
            NOERASE=noerase, $
            NOINTERPOLATE=nointerpolate, $
            KEEP_ASPECT=keep_aspect, $
            BOTTOM=bottom, $
            BETA=beta, $
            EXPONENT=exponent, $
            GAMMA=gamma, $
            MEAN=mean, $
            MISSING_COLOR=missing_color, $
            MISSING_INDEX=missing_index, $
            MISSING_VALUE=missing_value, $
            NCOLORS=ncolors, $
            SCALETYPE=scaletype, $
            SCLMIN=sclmin, $
            SCLMAX=sclmax, $
            SIGMA=sigma, $
            AXES=axes, $
            AXISCOLOR=axiscolor, $
            CHARSIZE=charsize, $
            CHARTHICK=charthick, $
            FONT=font, $
            TICKLEN=ticklen, $
            XCHARSIZE=xcharsize, $
            XGRIDSTYLE=xgridstyle, $
            XLOG=xlog, $
            XMINOR=xminor, $
            XTHICK=xthick, $
            XTICKFORMAT=xtickformat, $
            XTICKINTERVAL=xtickinterval, $
            XTICKLAYOUT=xticklayout, $
            XTICKLEN=xticklen, $
            XTICKNAME=xtickname, $
            XTICKS=xticks, $
            XTICKUNITS=xtickunits, $
            XTICKV=xtickv, $
            XTITLE=xtitle, $
            YCHARSIZE=ycharsize, $
            YGRIDSTYLE=ygridstyle, $
            YLOG=ylog, $
            YMINOR=yminor, $
            YTHICK=ythick, $
            YTICKFORMAT=ytickformat, $
            YTICKINTERVAL=ytickinterval, $
            YTICKLAYOUT=yticklayout, $
            YTICKLEN=yticklen, $
            YTICKNAME=ytickname, $
            YTICKS=yticks, $
            YTICKUNITS=ytickunits, $
            YTICKV=ytickv, $
            YTITLE=ytitle

  COMPILE_OPT idl2, HIDDEN
  ON_ERROR, 2

  ;; Initialize the global system variables
  IGL_InitSysVars

  ;; Check positional argument
  IF N_Elements(img) EQ 0 THEN Message, 'IMAGE argument is not set'
  dims = size(img)
  IF (dims[0] NE 2) AND (dims[0] NE 3) THEN Message, 'IMAGE argument must have 2 or 3 dimensions'

  ;; Default keyword values
  
  ;; General keywords
  IF N_Elements(charsize) EQ 0 THEN charsize = (!IGL_P.charsize LE 0) ? 1 : !IGL_P.charsize
  IF N_Elements(charthick) EQ 0 THEN charthick = (!IGL_P.charthick LE 0) ? 1 : !IGL_P.charthick
  IF N_Elements(color) EQ 0 THEN color = !IGL_P.axiscolor
  IF N_Elements(font) EQ 0 THEN font = !IGL_P.font
  IF N_Elements(position) EQ 0 THEN position = !IGL_P.position
  IF N_Elements(ticklen) EQ 0 THEN ticklen = (!IGL_P.ticklen LE 0) ? 0.02 : !IGL_P.ticklen

  ;; X axis keywords
  IF N_Elements(xcharsize) EQ 0 THEN xcharsize = (!IGL_X.charsize LE 0) ? 1 : !IGL_X.charsize
  IF N_Elements(xgridstyle) EQ 0 THEN xgridstyle = !IGL_X.gridstyle
  IF N_Elements(xminor) EQ 0 THEN xminor = !IGL_X.minor
  IF N_Elements(xrange) EQ 0 THEN xrange = !IGL_X.range
  IF N_Elements(xthick) EQ 0 THEN xthick = (!IGL_X.thick LE 0) ? 1 : !IGL_X.thick
  IF N_Elements(xtickformat) EQ 0 THEN xtickformat = !IGL_X.tickformat
  IF N_Elements(xtickinterval) EQ 0 THEN xtickinterval = !IGL_X.tickinterval
  IF N_Elements(xticklayout) EQ 0 THEN xticklayout = !IGL_X.ticklayout
  IF N_Elements(xticklen) EQ 0 THEN xticklen = (!IGL_X.ticklen LE 0) ? ticklen : !IGL_X.ticklen
  IF N_Elements(xtickname) EQ 0 THEN xtickname = !IGL_X.tickname
  IF N_Elements(xticks) EQ 0 THEN xticks = !IGL_X.ticks
  IF N_Elements(xtickunits) EQ 0 THEN xtickunits = !IGL_X.tickunits
  IF N_Elements(xtickv) EQ 0 THEN xtickv = !IGL_X.tickv
  IF N_Elements(xtitle) EQ 0 THEN xtitle = !IGL_X.title

  ;; Y axis keywords
  IF N_Elements(ycharsize) EQ 0 THEN ycharsize = (!IGL_Y.charsize LE 0) ? 1 : !IGL_Y.charsize
  IF N_Elements(ygridstyle) EQ 0 THEN ygridstyle = !IGL_Y.gridstyle
  IF N_Elements(yminor) EQ 0 THEN yminor = !IGL_Y.minor
  IF N_Elements(yrange) EQ 0 THEN yrange = !IGL_Y.range
  IF N_Elements(ythick) EQ 0 THEN ythick = (!IGL_Y.thick LE 0) ? 1 : !IGL_Y.thick
  IF N_Elements(ytickformat) EQ 0 THEN ytickformat = !IGL_Y.tickformat
  IF N_Elements(ytickinterval) EQ 0 THEN ytickinterval = !IGL_Y.tickinterval
  IF N_Elements(yticklayout) EQ 0 THEN yticklayout = !IGL_Y.ticklayout
  IF N_Elements(yticklen) EQ 0 THEN yticklen = (!IGL_Y.ticklen LE 0) ? ticklen : !IGL_Y.ticklen
  IF N_Elements(ytickname) EQ 0 THEN ytickname = !IGL_Y.tickname
  IF N_Elements(yticks) EQ 0 THEN yticks = !IGL_Y.ticks
  IF N_Elements(ytickunits) EQ 0 THEN ytickunits = !IGL_Y.tickunits
  IF N_Elements(ytickv) EQ 0 THEN ytickv = !IGL_Y.tickv
  IF N_Elements(ytitle) EQ 0 THEN ytitle = !IGL_Y.title

  ;; Make sure we have a valid position vector
  IF Total(position) LE 0 THEN pos = [0.1,0.1,0.9,0.9] ELSE pos = position
  IF (pos[0] GE pos[2]) OR (pos[1] GE pos[3]) THEN Message, 'Invalid POSITION array'

  ;; Save device settings and switch to window device
  entryDevice = !d.name
  IF (strupcase(!version.os_family) EQ 'UNIX') THEN set_plot, 'X'
  IF (strupcase(!version.os_family) EQ 'WINDOWS') THEN set_plot, 'WIN'

  ;; Get current window, create one if none is available
  currentWindow = IGL_GetCurrentWindow()
  IF OBJ_ISA_VALID(currentWindow,'IGL_Window') EQ 0 THEN currentWindow = obj_new('IGL_Window')

  ;; Create a new graphic to hold the image
  graphic = obj_new('IGL_Graphic',POSITION=pos)

  ;; Create a new image object
  imageObject = obj_new('IGL_Image',img, $
                        POSITION=pos, $
                        SELECTABLE=0, $
                        KEEP_ASPECT=Keyword_Set(keep_aspect), $
                        NOINTERPOLATE=Keyword_Set(nointerpolate), $
                        BOTTOM=bottom, $
                        BETA=beta, $
                        EXPONENT=exponent, $
                        GAMMA=gamma, $
                        MEAN=mean, $
                        MISSING_COLOR=missing_color, $
                        MISSING_INDEX=missing_index, $
                        MISSING_VALUE=missing_value, $
                        NCOLORS=ncolors, $
                        SCALETYPE=scaletype, $
                        SCLMIN=sclmin, $
                        SCLMAX=sclmax, $
                        SIGMA=sigma $
                        )

  ;; Calculate a coordinate system for the image  
  imageObject -> GetProperty, Xsize=Xsize, Ysize=Ysize ; The default is to use the size of the image
  IF (xrange[0] EQ xrange[1]) THEN xrange = [0,(Xsize-1) > 1]
  IF (yrange[0] EQ yrange[1]) THEN yrange = [0,(Ysize-1) > 1]

  ;; Create the coordinate object
  coords = obj_new('IGL_Coord', $
                   POSITION=pos, $
                   XRANGE=xrange, $
                   YRANGE=yrange, $
                   XLOG=Keyword_Set(xlog), $
                   YLOG=Keyword_Set(ylog), $
                   STATIC=Keyword_Set(static) $
                   )

  ;; Set object properties
  imageObject -> SetProperty, Coord_Object=coords, INITIAL_Xrange=xrange, INITIAL_Yrange=Yrange

  ;; Do we need axes ?

  IF Keyword_Set(axes) THEN BEGIN
     
     ;; X AXIS
     
     bottomAxis = obj_new('IGL_Axis', $
                          COORD_OBJECT=coords, $
                          XLOC=0.0, $
                          YLOC=0.0, $
                          DIRECTION=0, $
                          TICKDIR=0, $
                          COLOR=axiscolor, $
                          CHARSIZE=(xcharsize*charsize), $
                          CHARTHICK=charthick, $
                          FONT=(font+1), $
                          GRIDSTYLE=xgridstyle, $
                          THICK=xthick, $
                          MINOR=xminor, $
                          TICKFORMAT=xtickformat, $
                          TICKNAME=xtickname, $
                          TICKINTERVAL=xtickinterval, $
                          TICKLAYOUT=xticklayout, $
                          TICKLEN=xticklen, $
                          TICKS=xticks, $
                          TICKUNITS=xtickunits, $
                          TICKV=xtickv $
                         )

     ;; Create the axis title if needed
     IF (xtitle NE '') THEN BEGIN
        
        ;; Calculate the title position
        xloc = 0.5
        yloc = pos[1]-3.0*charsize*xcharsize*!D.Y_CH_SIZE/float(Ysize)
        yloc = (yloc-pos[1])/(pos[3]-pos[1]) ; Convert to graphic coordinates
        
        ;; Initialize the text object
        textObject = obj_new('IGL_Text',xtitle, $
                             X=xloc, $
                             Y=yloc, $
                             ALIGNMENT=1, $
                             CHARSIZE=charsize*xcharsize, $
                             CLIP=0, $
                             COLOR=axiscolor, $
                             FONT=(font+1), $
                             THICKNESS=charthick, $
                             ORIENTATION=0.0 $
                            )
        
        ;; Get the graphic's coordinate system to assign it to the text object
        graphic -> GetProperty, Coord_Object=graphicCoords
        
        ;; Set object properties
        textObject -> SetProperty, Name='Axis Title', Coord_Object=graphicCoords
        
        ;; Add the title to the axis
        bottomAxis -> Add, textObject
        
     ENDIF                        

     topAxis = obj_new('IGL_Axis', $
                       COORD_OBJECT=coords, $
                       XLOC=0.0, $
                       YLOC=1.0, $
                       DIRECTION=0, $
                       TICKDIR=1, $
                       COLOR=axiscolor, $
                       CHARSIZE=(xcharsize*charsize), $
                       CHARTHICK=charthick, $
                       FONT=(font+1), $
                       GRIDSTYLE=xgridstyle, $
                       THICK=xthick, $
                       MINOR=xminor, $
                       TICKFORMAT='(A1)', $
                       TICKNAME=xtickname, $
                       TICKINTERVAL=xtickinterval, $
                       TICKLAYOUT=xticklayout, $
                       TICKLEN=xticklen, $
                       TICKS=xticks, $
                       TICKUNITS=xtickunits, $
                       TICKV=xtickv $
                      )

     ;; Add the axis to the image
     imageObject -> Add, bottomAxis
     imageObject -> Add, topAxis

     
     ;; Y AXIS

     leftAxis = obj_new('IGL_Axis', $
                        COORD_OBJECT=coords, $
                        XLOC=0.0, $
                        YLOC=0.0, $
                        DIRECTION=1, $
                        TICKDIR=0, $
                        COLOR=axiscolor, $
                        CHARSIZE=(ycharsize*charsize), $
                        CHARTHICK=charthick, $
                        FONT=(font+1), $
                        GRIDSTYLE=ygridstyle, $
                        THICK=ythick, $
                        MINOR=yminor, $
                        TICKFORMAT=ytickformat, $
                        TICKNAME=ytickname, $
                        TICKINTERVAL=ytickinterval, $
                        TICKLAYOUT=yticklayout, $
                        TICKLEN=yticklen, $
                        TICKS=yticks, $
                        TICKUNITS=ytickunits, $
                        TICKV=ytickv $
                       )

     ;; Create the axis title if needed
     IF (ytitle NE '') THEN BEGIN
        
        ;; Calculate the title position
        yloc = 0.5
        middle = 0.5*(yrange[1]+yrange[0])
        IF middle NE 0 THEN ndigits = Floor(alog10(Abs(middle))+1) ELSE ndigits = 1
        xloc = pos[0]-(ndigits+2.5)*charsize*ycharsize*!D.X_CH_SIZE/float(Xsize)
        xloc = (xloc-pos[0])/(pos[2]-pos[0]) ; Convert to graphic coordinates
        
        ;; Initialize the text object
        textObject = obj_new('IGL_Text',ytitle, $
                             X=xloc, $
                             Y=yloc, $
                             ALIGNMENT=1, $
                             CHARSIZE=charsize*ycharsize, $
                             CLIP=0, $
                             COLOR=axiscolor, $
                             FONT=(font+1), $
                             THICKNESS=charthick, $
                             ORIENTATION=90.0 $
                            )
        
        ;; Get the graphic's coordinate system to assign it to the text object
        graphic -> GetProperty, Coord_Object=graphicCoords
        
        ;; Set object properties
        textObject -> SetProperty, Name='Axis Title', Coord_Object=graphicCoords
        
        ;; Add the title to the axis
        leftAxis -> Add, textObject
        
     ENDIF
     
     rightAxis = obj_new('IGL_Axis', $
                         COORD_OBJECT=coords, $
                         XLOC=1.0, $
                         YLOC=0.0, $
                         DIRECTION=1, $
                         TICKDIR=1, $
                         COLOR=axiscolor, $
                         CHARSIZE=(ycharsize*charsize), $
                         CHARTHICK=charthick, $
                         FONT=(font+1), $
                         GRIDSTYLE=ygridstyle, $
                         THICK=ythick, $
                         MINOR=yminor, $
                         TICKFORMAT='(A1)', $
                         TICKNAME=ytickname, $
                         TICKINTERVAL=ytickinterval, $
                         TICKLAYOUT=yticklayout, $
                         TICKLEN=yticklen, $
                         TICKS=yticks, $
                         TICKUNITS=xtickunits, $
                         TICKV=ytickv $
                        )

     
     ;; Add the axis to the image
     imageObject -> Add, leftAxis
     imageObject -> Add, rightAxis

  ENDIF

  ;; Do we need to erase the window ?
  IF Keyword_Set(noerase) EQ 0 THEN currentWindow -> ClearWindow, /NoRefresh

  ;; Add the image to the graphic
  graphic -> Add, imageObject
     
  ;; Update the coordinate system
  IF Keyword_Set(save) THEN IGL_SetSysVars, 'currentCoord', coords

  ;; Add the graphic to the window
  currentWindow -> AddGraphic, graphic

  ;; Refresh the property editor
  currentWindow -> GetProperty, PropertyPanel=PropertyPanel
  IF OBJ_VALID(PropertyPanel) THEN PropertyPanel -> UpdateTreeBrowser
  
  ;; Draw window's content
  IF Keyword_set(nodraw) EQ 0 THEN currentWindow -> DrawWindow, /Buffered_Output

  ;; Restore device
  Set_Plot, entryDevice  

END
